package com.zhiletu.collect.splitword;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.zhiletu.collect.DictTool;
import com.zhiletu.collect.util.ParseString;
import org.ansj.domain.Term;
import org.ansj.splitWord.analysis.ToAnalysis;

/**
 *
 * @Title: SplitWord.java
 * @Package com.zhiletu.collect.splitword
 * @Description: 中文分词与词频统计
 * @author zhiletu.com
 * @date 2021年3月11日
 * @version V1.0
 */
public class SplitWord {

	/**
	 * 得到长度超过指定长度的词汇
	 *
	 * @param list
	 * @param length
	 * @return
	 */
	public static List<String> getWordgtlen(final List<Term> list, int length) {
		List<String> wlist = new ArrayList<String>();
		for (Term t : list) {
			String name = t.getName();
			if (name.length() >= length)
				wlist.add(name);
		}

		return wlist;
	}

	/**
	 * 获取长度超过指定长度的词组项 同时过滤掉标点符号和英文
	 *
	 * @param list
	 * @param length
	 * @param filterWild 过滤标点符号、特殊符号和英文
	 * @return
	 */
	public static List<Term> getTermgtlen(final List<Term> list, int length, boolean filterWild) {
		List<Term> wlist = new ArrayList<Term>();
		if (filterWild)
			for (Term t : list) {
				String naure = t.getNatureStr();
				if ("w".equals(naure) || "x".equals(naure) || "en".equals(naure))
					continue;
				String name = t.getName();
				if (name.length() >= length)
					wlist.add(t);
				name = null;
			}
		else
			for (Term t : list) {
				String name = t.getName();
				if (name.length() >= length)
					wlist.add(t);
			}

		return wlist;
	}

	/**
	 * 获取长度超过指定长度的词组项 同时设置是否过滤掉标点符号和英文
	 *
	 * @param list
	 * @param length
	 * @param filterWild 过滤标点符号、特殊符号
	 * @param filterEn 是否过滤英文
	 * @return
	 */
	public static List<Term> getTermFiltEngtlen(final List<Term> list, int length, boolean filterWild, boolean filterEn) {
		List<Term> wlist = new ArrayList<Term>();
		if (filterWild)
			for (Term t : list) {
				String naure = t.getNatureStr();
				if ("w".equals(naure) || "x".equals(naure) || (filterEn && "en".equals(naure)))
					continue;
				String name = t.getName();
				if (name.length() >= length)
					wlist.add(t);
				name = null;
			}
		else
			for (Term t : list) {
				String naure = t.getNatureStr();
				if (filterEn && "en".equals(naure))
					continue;
				String name = t.getName();
				if (name.length() >= length)
					wlist.add(t);
			}

		return wlist;
	}

	/**
	 * 获取长度超过指定长度、指定词性的词组项 同时过滤掉标点符号
	 *
	 * @param content
	 * @param length
	 * @param filterWild 是否过滤标点符号、特殊符号
	 * @return
	 */
	public static List<Term> getTermByLengthNaure(String content, int length, String naureStr, boolean filterWild) {
		// 先进行中文分词 
		List<Term> list = (List<Term>) ToAnalysis.parse(content);

		if (list == null || list.isEmpty()) {
			return null;
		}

		List<Term> wlist = new ArrayList<Term>();
		if (filterWild)
			for (Term t : list) {
				String naure = t.getNatureStr();
				if ("w".equals(naure) || "x".equals(naure) || "en".equals(naure))
					continue;
				if (!ParseString.isNull(naureStr) && !naure.equals(naureStr)) {
					continue;
				}
				String name = t.getName();
				if (name.length() >= length)
					wlist.add(t);
				name = null;
			}
		else
			for (Term t : list) {
				String naure = t.getNatureStr();
				if (!ParseString.isNull(naureStr) && !naure.equals(naureStr)) {
					continue;
				}
				String name = t.getName();
				if (name.length() >= length)
					wlist.add(t);
			}

		return wlist;
	}

	/**
	 * 对分词词组进行按字面长度降序排列
	 */
	public static List<Term> sortListTerm(List<Term> term) {
		List<Term> temp = term;
		Collections.sort(temp, new TermComparator());
		return temp;
	}

	/**
	 * 重组一个词汇集团，按词性、字符长度、字面值为层次形成词汇组织结构
	 *
	 * @param list
	 * @return
	 */
	protected static Map<String, Map<String, Map<String, WordFilter>>> resetWordPlus(
			final List<Term> list) {
		// 词汇母体
		Map<String, Map<String, Map<String, WordFilter>>> wordMapAll = new HashMap<String, Map<String, Map<String, WordFilter>>>();
		for (Term t : list) {
			// 词性为索引
			String nature = t.getNatureStr();
			// 字面为索引
			String name = t.getName();
			// 长度为索引
			String length = Integer.toString(name.length());
			// 以词性为索引细分 到长度颗粒度
			Map<String, Map<String, WordFilter>> wordMapWithN = wordMapAll
					.get(nature);
			if (wordMapWithN != null) {
				// 在指定词性下以字符长度为索引细分 到字面值颗粒度
				Map<String, WordFilter> wordMapWithNL = wordMapWithN
						.get(length);
				if (wordMapWithNL != null) {
					// 最终得到指定词性、指定字符长度、指定字面值的词组过滤器
					WordFilter wordfilter = wordMapWithNL.get(name);
					if (wordfilter != null) {
						// 不为空，说明这个长度的词汇曾来过！到此一游，+1.
						++wordfilter.count;
					}
					else {
						// it's a fresh one!
						wordfilter = new WordFilter(nature, name, 1);
						wordMapWithNL.put(name, wordfilter);
					}
				}
				else {
					// the other length
					WordFilter tempfilter = new WordFilter(nature, name, 1);
					wordMapWithNL = new HashMap<String, WordFilter>();
					wordMapWithNL.put(name, tempfilter);
					wordMapWithN.put(length, wordMapWithNL);
				}
			}
			else {
				// 这个词性第一次出现！收之！
				WordFilter tempfilter = new WordFilter(nature, name, 1);
				Map<String, WordFilter> tempWL = new HashMap<String, WordFilter>();
				tempWL.put(name, tempfilter);
				wordMapWithN = new HashMap<String, Map<String, WordFilter>>();
				wordMapWithN.put(length, tempWL);
				// 放入母体
				wordMapAll.put(nature, wordMapWithN);
			}
		}

		return wordMapAll;
	}

	/**
	 * 得到一个带词频的词汇表 （包含词性、字符长度）
	 *
	 * @param wordMapAll
	 * @return LinkedHashMap实例
	 */
	protected static Map<String, WordFilter> collectWordFilter(
			final Map<String, Map<String, Map<String, WordFilter>>> wordMapAll) {
		// 保存结果
		Map<String, WordFilter> wordMap = new HashMap<String, WordFilter>();

		Iterator<Map.Entry<String, Map<String, Map<String, WordFilter>>>> iter = wordMapAll.entrySet().iterator();
		while (iter.hasNext()) {
			Iterator<Map.Entry<String, Map<String, WordFilter>>> iter1 = iter.next().getValue().entrySet().iterator();
			while (iter1.hasNext()) {
				wordMap.putAll(iter1.next().getValue());
			}
		}
		return wordMap;
	}

	/**
	 * 得到一个按词频排序（按词频由低到高排序）的集合（同时可以获取词性、长度等信息）
	 *
	 * @param map
	 * @return
	 */
	protected static Map<String, WordFilter> sortMapByFrequency(final Map<String, WordFilter> map) {
		// 调用一个高大上的排序器
		MapSort<WordFilter> mapSort = new MapSort<WordFilter>();

		return mapSort.sortMapByValue(map);
	}

	/**
	 * 按百分比取部分词汇(倒序为高频词汇、正序为低频词汇)
	 * @param map  词汇频率表
	 * @param per  获取总数的百分比
	 * @param desc 是否倒序取元素
	 * @return
	 */
	protected static Map<String, WordFilter> getMapByPer(final Map<String, WordFilter> map, int per, boolean desc) {

		if (map == null || map.size() == 0)
			return null;

		Map<String, WordFilter> newMap = new LinkedHashMap<String, WordFilter>();

		if (per == 0)
			throw new RuntimeException("采样百分比不能为0");

		if (per > 100)
			throw new RuntimeException("采样百分比不能超过100");

		int getLen = map.size() * per / 100;

		getLen = getLen == 0 ? 1 : getLen;

		// 伟大的泛型，伟大的集合框架
		List<Map.Entry<String, WordFilter>> entryList = new ArrayList<Map.Entry<String, WordFilter>>(map.entrySet());

		if (desc) // 倒序取
			for (int x = entryList.size() - 1; x >= entryList.size() - getLen; x--) {
				Map.Entry<String, WordFilter> tempEntry = entryList.get(x);
				newMap.put(tempEntry.getKey(), tempEntry.getValue());
			}
		else    // 正序取
			for (int x = 0; x < getLen; x++) {
				Map.Entry<String, WordFilter> tempEntry = entryList.get(x);
				newMap.put(tempEntry.getKey(), tempEntry.getValue());
			}

		entryList = null;// 置空

		return newMap;
	}

	/**
	 * 按排名取部分词汇(倒序为高频词汇、正序为低频词汇)
	 * @param map  词汇频率表
	 * @param top  排名
	 * @param desc 是否倒序取元素
	 * @return
	 */
	protected static Map<String, WordFilter> getMapByTop(final Map<String, WordFilter> map, int top, boolean desc) {

		if (map == null || map.size() == 0)
			return null;

		Map<String, WordFilter> newMap = new LinkedHashMap<String, WordFilter>();

		if (top == 0)
			throw new RuntimeException("采样排名从1开始");

		if (top > map.size())
			top = map.size();

		// 伟大的泛型，伟大的集合框架
		List<Map.Entry<String, WordFilter>> entryList = new ArrayList<Map.Entry<String, WordFilter>>(map.entrySet());

		if (desc) // 倒序取
			for (int x = entryList.size() - 1; x >= entryList.size() - top; x--) {
				Map.Entry<String, WordFilter> tempEntry = entryList.get(x);
				newMap.put(tempEntry.getKey(), tempEntry.getValue());
			}
		else    // 正序取
			for (int x = 0; x < top; x++) {
				Map.Entry<String, WordFilter> tempEntry = entryList.get(x);
				newMap.put(tempEntry.getKey(), tempEntry.getValue());
			}

		entryList = null; // 置空

		return newMap;
	}

	/**
	 *
	 * @Title: getTermsFiltEn
	 * @Description: 获取长度2以上的词组列表，过滤标点符号，并可设置是否过滤英文。
	 * @param article 文章内容
	 * @param isFiltEn 是否过滤英文
	 * @return 词组列表
	 * @return List<Term>
	 */
	public static List<Term> getTermsFiltEn(String article, boolean isFiltEn) {
		// 先进行中文分词
		List<Term> list = (List<Term>) ToAnalysis.parse(article);

		if (list == null || list.isEmpty()) {
			return null;
		}

		// 取长度2以上的词组，不考虑单字 和 标点符号 、英文
		List<Term> list2w = SplitWord.getTermFiltEngtlen(list, 2, true, isFiltEn);
		if (list2w.isEmpty()) {
			return null;
		}

		list = null;

		return list2w;
	}

	/**
	 * 按百分比取部分词汇(倒序为高频词汇、正序为低频词汇)
	 * @param article 一篇文章
	 * @param per  获取总数的百分比
	 * @param desc 是否倒序取元素
	 * @return
	 */
	public static Map<String, WordFilter> getMapByPer(String article, int per, boolean desc) {

		// 先进行中文分词
		//System.out.println("文章原稿：\n"+article); 

		// 取长度2以上的词组，不考虑单字 和 标点符号 、英文
		List<Term> list2w = SplitWord.getTermsFiltEn(article, true);
		SplitWord.saveWordDict(list2w);

		if (per == 0) {
			return null;
		}

		// 按词性、字符长度、字面值为层次(索引)形成词汇组织结构
		Map<String, Map<String, Map<String, WordFilter>>> wordMapAll = SplitWord.resetWordPlus(list2w);
		list2w = null;

		// 根据分层级索引结构得到一个带词频的词汇表 （包含词性、字符长度）（未按词频排序）
		Map<String, WordFilter> wordMap = SplitWord.collectWordFilter(wordMapAll);
		wordMapAll = null;

		// 得到一个按词频排序（按词频由低到高排序）的集合（同时可以获取词性、长度等信息）
		Map<String, WordFilter> map = SplitWord.sortMapByFrequency(wordMap);
		wordMap = null;

		// 按一定百分比取部分词汇(倒序为高频词汇、正序为低频词汇)
		Map<String, WordFilter> newMap = SplitWord.getMapByPer(map, per, desc);
		map = null;

		return newMap;
	}

	/**
	 * 按百分比取部分词汇(倒序为高频词汇、正序为低频词汇)
	 * @param list2w 词组列表，长度在2以上
	 * @param per  获取总数的百分比
	 * @param desc 是否倒序取元素
	 * @return
	 */
	public static Map<String, WordFilter> getMapByPer(List<Term> list2w, int per, boolean desc) {

		SplitWord.saveWordDict(list2w);

		if (per == 0) {
			return null;
		}

		// 按词性、字符长度、字面值为层次(索引)形成词汇组织结构
		Map<String, Map<String, Map<String, WordFilter>>> wordMapAll = SplitWord.resetWordPlus(list2w);
		list2w = null;

		// 根据分层级索引结构得到一个带词频的词汇表 （包含词性、字符长度）（未按词频排序）
		Map<String, WordFilter> wordMap = SplitWord.collectWordFilter(wordMapAll);
		wordMapAll = null;

		// 得到一个按词频排序（按词频由低到高排序）的集合（同时可以获取词性、长度等信息）
		Map<String, WordFilter> map = SplitWord.sortMapByFrequency(wordMap);
		wordMap = null;

		// 按一定百分比取部分词汇(倒序为高频词汇、正序为低频词汇)
		Map<String, WordFilter> newMap = SplitWord.getMapByPer(map, per, desc);
		map = null;

		return newMap;
	}

	/**
	 * 按百分比获取词汇列表(倒序为高频词汇、正序为低频词汇)，不保存词典
	 * @param article 一篇文章
	 * @param per  获取总数的百分比
	 * @param desc 是否倒序取元素，倒序取高频词汇，正序取低频词汇
	 * @return
	 */
	public static Map<String, WordFilter> getMapByPerNoDict(String article, int per, boolean desc) {

		// 取长度2以上的词组，不考虑单字 和 标点符号 、英文
		List<Term> list2w = SplitWord.getTermsFiltEn(article, true);

		if (per == 0) {
			return null;
		}

		// 按词性、字符长度、字面值为层次(索引)形成词汇组织结构
		Map<String, Map<String, Map<String, WordFilter>>> wordMapAll = SplitWord.resetWordPlus(list2w);
		list2w = null;

		// 根据分层级索引结构得到一个带词频的词汇表 （包含词性、字符长度）（未按词频排序）
		Map<String, WordFilter> wordMap = SplitWord.collectWordFilter(wordMapAll);
		wordMapAll = null;

		// 得到一个按词频排序（按词频由低到高排序）的集合（同时可以获取词性、长度等信息）
		Map<String, WordFilter> map = SplitWord.sortMapByFrequency(wordMap);
		wordMap = null;

		// 按一定百分比取部分词汇(倒序为高频词汇、正序为低频词汇)
		Map<String, WordFilter> newMap = SplitWord.getMapByPer(map, per, desc);
		map = null;

		return newMap;
	}

	/**
	 * 按百分比获取词汇列表(倒序为高频词汇、正序为低频词汇)，不保存词典
	 * @param list2w 词组列表，长度在2以上
	 * @param per  获取总数的百分比
	 * @param desc 是否倒序取元素，倒序取高频词汇，正序取低频词汇
	 * @return
	 */
	public static Map<String, WordFilter> getMapByPerNoDict(List<Term> list2w, int per, boolean desc) {

		if (per == 0) {
			return null;
		}

		// 按词性、字符长度、字面值为层次(索引)形成词汇组织结构
		Map<String, Map<String, Map<String, WordFilter>>> wordMapAll = SplitWord.resetWordPlus(list2w);
		list2w = null;

		// 根据分层级索引结构得到一个带词频的词汇表 （包含词性、字符长度）（未按词频排序）
		Map<String, WordFilter> wordMap = SplitWord.collectWordFilter(wordMapAll);
		wordMapAll = null;

		// 得到一个按词频排序（按词频由低到高排序）的集合（同时可以获取词性、长度等信息）
		Map<String, WordFilter> map = SplitWord.sortMapByFrequency(wordMap);
		wordMap = null;

		// 按一定百分比取部分词汇(倒序为高频词汇、正序为低频词汇)
		Map<String, WordFilter> newMap = SplitWord.getMapByPer(map, per, desc);
		map = null;

		return newMap;
	}

	/**
	 * 按排名获取词汇列表(倒序为高频词汇、正序为低频词汇)，不保存词典
	 * @param article 一篇文章
	 * @param top  获取总数的百分比
	 * @param desc 是否倒序取元素
	 * @return
	 */
	public static Map<String, WordFilter> getMapByTop(String article, int top, boolean desc) {

		// 先进行中文分词		
		// 取长度2以上的词组，不考虑单字 和 标点符号 、英文
		List<Term> list2w = SplitWord.getTermsFiltEn(article, true);

		// 按词性、字符长度、字面值为层次(索引)形成词汇组织结构
		Map<String, Map<String, Map<String, WordFilter>>> wordMapAll = SplitWord.resetWordPlus(list2w);
		list2w = null;

		// 根据分层级索引结构得到一个带词频的词汇表 （包含词性、字符长度）（未按词频排序）
		Map<String, WordFilter> wordMap = SplitWord.collectWordFilter(wordMapAll);
		wordMapAll = null;

		// 得到一个按词频排序（按词频由低到高排序）的集合（同时可以获取词性、长度等信息）
		Map<String, WordFilter> map = SplitWord.sortMapByFrequency(wordMap);
		wordMap = null;

		// 按排名取部分词汇(倒序为高频词汇、正序为低频词汇)
		Map<String, WordFilter> newMap = SplitWord.getMapByTop(map, top, desc);
		map = null;

		return newMap;
	}

	/**
	 * 按排名获取词汇列表(倒序为高频词汇、正序为低频词汇)，不保存词典
	 * @param list2w 词组列表，长度在2以上
	 * @param top  正序或倒序前top个词汇，从1开始，超出列表长度时取全集
	 * @param desc 是否倒序取元素，倒序取高频词汇，正序取低频词汇
	 * @return
	 */
	public static Map<String, WordFilter> getMapByTop(List<Term> list2w, int top, boolean desc) {

		// 按词性、字符长度、字面值为层次(索引)形成词汇组织结构
		Map<String, Map<String, Map<String, WordFilter>>> wordMapAll = SplitWord.resetWordPlus(list2w);
		list2w = null;

		// 根据分层级索引结构得到一个带词频的词汇表 （包含词性、字符长度）（未按词频排序）
		Map<String, WordFilter> wordMap = SplitWord.collectWordFilter(wordMapAll);
		wordMapAll = null;

		// 得到一个按词频排序（按词频由低到高排序）的集合（同时可以获取词性、长度等信息）
		Map<String, WordFilter> map = SplitWord.sortMapByFrequency(wordMap);
		wordMap = null;

		// 按排名取部分词汇(倒序为高频词汇、正序为低频词汇)
		Map<String, WordFilter> newMap = SplitWord.getMapByTop(map, top, desc);
		map = null;

		return newMap;
	}

	/**
	 * 按排名获取文章的标签（热词或冷词）
	 * @param article 一篇文章
	 * @param top  正序或倒序前top个词汇，从1开始，超出列表长度时取全集
	 * @param desc 是否倒序取元素，倒序取高频词汇，正序取低频词汇
	 * @return
	 */
	public static List<String> getWordTagByTop(String article, int top, boolean desc) {
		List<String> tags = new ArrayList<String>();
		Map<String, WordFilter> map = SplitWord.getMapByTop(article, top, desc);
		if (map == null || map.isEmpty()) {
			return tags;
		}

		// 根据高频词汇取同义词库
		Iterator<Map.Entry<String, WordFilter>> iter = map.entrySet().iterator();
		while (iter.hasNext()) {
			WordFilter word = null;
			try {
				word = iter.next().getValue();
			}
			catch (Exception e) {
				word = new WordFilter("", "", 0);
				e.printStackTrace();
			}

			tags.add(word.getWordName());
		}

		return tags;
	}


	/**
	 * 按排名获取词组的标签（热词或冷词）
	 * @param list2w 词组列表，长度在2以上
	 * @param top  正序或倒序前top个词汇，从1开始，超出列表长度时取全集
	 * @param desc 是否倒序取元素，倒序取高频词汇，正序取低频词汇
	 * @return
	 */
	public static List<String> getWordTagByTop(List<Term> list2w, int top, boolean desc) {
		List<String> tags = new ArrayList<String>();
		Map<String, WordFilter> map = SplitWord.getMapByTop(list2w, top, desc);
		if (map == null || map.isEmpty()) {
			return tags;
		}

		// 根据高频词汇取同义词库
		Iterator<Map.Entry<String, WordFilter>> iter = map.entrySet().iterator();
		while (iter.hasNext()) {
			WordFilter word = null;
			try {
				word = iter.next().getValue();
			}
			catch (Exception e) {
				word = new WordFilter("", "", 0);
				e.printStackTrace();
			}

			tags.add(word.getWordName());
		}

		return tags;
	}


	/**
	 *
	 * @Title: saveWordDict
	 * @Description: 保存词组到词典，嵌入字典库保存（凡是采集任务一律分词并存入词典为机器人准备第一手资料）。
	 * @param terms
	 * @return
	 * @return boolean
	 */
	public static boolean saveWordDict(List<Term> terms) {
		if (terms == null || terms.isEmpty()) {
			throw new RuntimeException("词组为空，请检查terms。");
		}
		// 分词排重
		List<Term> temp = new ArrayList<Term>();
		for (Term t : terms) {
			if (!temp.contains(t)) {
				temp.add(t);
				try {
					DictTool.queryDict(t.getName());
				}
				catch (Exception e) {
					e.printStackTrace();
				}
				t = null;
			}
		}
		temp = null;

		return true;
	}
}

/**
 *
 * @Title: SplitWord.java
 * @Package com.zhiletu.collect.splitword
 * @Description: 针对分词对象的字面长度进行排序
 * @author zhiletu.com
 * @date 2021年3月11日
 * @version V1.0
 */
class TermComparator implements Comparator<Term> {

	@Override
	public int compare(Term o1, Term o2) {
		return o1.getName().length() <= o2.getName().length() ? 1 : 0;
	}
}
/*
 * 词性编码 词性名称 注 解 Ag
 *
 * 形语素
 *
 * 形容词性语素。形容词代码为 a，语素代码ｇ前面置以A。
 *
 *
 * a
 *
 * 形容词
 *
 * 取英语形容词 adjective的第1个字母。
 *
 *
 * ad
 *
 * 副形词
 *
 * 直接作状语的形容词。形容词代码 a和副词代码d并在一起。
 *
 *
 * an
 *
 * 名形词
 *
 * 具有名词功能的形容词。形容词代码 a和名词代码n并在一起。
 *
 *
 * b
 *
 * 区别词
 *
 * 取汉字"别"的声母。
 *
 *
 * c
 *
 * 连词
 *
 * 取英语连词 conjunction的第1个字母。
 *
 *
 * dg
 *
 * 副语素
 *
 * 副词性语素。副词代码为 d，语素代码ｇ前面置以D。
 *
 *
 * d
 *
 * 副词
 *
 * 取 adverb的第2个字母，因其第1个字母已用于形容词。
 *
 *
 * e
 *
 * 叹词
 *
 * 取英语叹词 exclamation的第1个字母。
 *
 *
 * f
 *
 * 方位词
 *
 * 取汉字"方"
 *
 *
 * g
 *
 * 语素
 *
 * 绝大多数语素都能作为合成词的"词根"，取汉字"根"的声母。
 *
 *
 * h
 *
 * 前接成分
 *
 * 取英语 head的第1个字母。
 *
 *
 * i
 *
 * 成语
 *
 * 取英语成语 idiom的第1个字母。
 *
 *
 * j
 *
 * 简称略语
 *
 * 取汉字"简"的声母。
 *
 *
 * k
 *
 * 后接成分
 *
 *
 *
 *
 * l
 *
 * 习用语
 *
 * 习用语尚未成为成语，有点"临时性"，取"临"的声母。
 *
 *
 * m
 *
 * 数词
 *
 * 取英语 numeral的第3个字母，n，u已有他用。
 *
 *
 * Ng
 *
 * 名语素
 *
 * 名词性语素。名词代码为 n，语素代码ｇ前面置以N。
 *
 *
 * n
 *
 * 名词
 *
 * 取英语名词 noun的第1个字母。
 *
 *
 * nr
 *
 * 人名
 *
 * 名词代码 n和"人(ren)"的声母并在一起。
 *
 *
 * ns
 *
 * 地名
 *
 * 名词代码 n和处所词代码s并在一起。
 *
 *
 * nt
 *
 * 机构团体
 *
 * "团"的声母为 t，名词代码n和t并在一起。
 *
 *
 * nz
 *
 * 其他专名
 *
 * "专"的声母的第 1个字母为z，名词代码n和z并在一起。
 *
 *
 * o
 *
 * 拟声词
 *
 * 取英语拟声词 onomatopoeia的第1个字母。
 *
 *
 * p
 *
 * 介词
 *
 * 取英语介词 prepositional的第1个字母。
 *
 *
 * q
 *
 * 量词
 *
 * 取英语 quantity的第1个字母。
 *
 *
 * r
 *
 * 代词
 *
 * 取英语代词 pronoun的第2个字母,因p已用于介词。
 *
 *
 * s
 *
 * 处所词
 *
 * 取英语 space的第1个字母。
 *
 *
 * tg
 *
 * 时语素
 *
 * 时间词性语素。时间词代码为 t,在语素的代码g前面置以T。
 *
 *
 * t
 *
 * 时间词
 *
 * 取英语 time的第1个字母。
 *
 *
 * u
 *
 * 助词
 *
 * 取英语助词 auxiliary
 *
 *
 * vg
 *
 * 动语素
 *
 * 动词性语素。动词代码为 v。在语素的代码g前面置以V。
 *
 *
 * v
 *
 * 动词
 *
 * 取英语动词 verb的第一个字母。
 *
 *
 * vd
 *
 * 副动词
 *
 * 直接作状语的动词。动词和副词的代码并在一起。
 *
 *
 * vn
 *
 * 名动词
 *
 * 指具有名词功能的动词。动词和名词的代码并在一起。
 *
 *
 * w
 * ----------------------------------------------------------------------------
 * -------------------1
 *
 * 标点符号
 *
 *
 *
 *
 * x------------------------------------------------------------------------------
 * --------------------2
 *
 * 非语素字
 *
 * 非语素字只是一个符号，字母 x通常用于代表未知数、符号。
 *
 *
 * y
 *
 * 语气词
 *
 * 取汉字"语"的声母。
 *
 *
 * z
 *
 * 状态词
 *
 * 取汉字"状"的声母的前一个字母。
 *
 *
 * un
 *
 * 未知词
 *
 * 不可识别词及用户自定义词组。取英文Unkonwn首两个字母。(非北大标准，CSW分词中定义)
 */